package xyz.qy.implatform.service.impl;

import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Splitter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import xyz.qy.imclient.IMClient;
import xyz.qy.imcommon.contant.IMRedisKey;
import xyz.qy.imcommon.enums.IMTerminalType;
import xyz.qy.imcommon.model.IMGroupMessage;
import xyz.qy.imcommon.model.IMRegionGroupMessage;
import xyz.qy.imcommon.model.IMUserInfo;
import xyz.qy.implatform.dto.RegionGroupMessageDTO;
import xyz.qy.implatform.entity.RegionGroup;
import xyz.qy.implatform.entity.RegionGroupMember;
import xyz.qy.implatform.entity.RegionGroupMessage;
import xyz.qy.implatform.entity.User;
import xyz.qy.implatform.enums.MessageStatus;
import xyz.qy.implatform.enums.MessageType;
import xyz.qy.implatform.enums.ResultCode;
import xyz.qy.implatform.exception.GlobalException;
import xyz.qy.implatform.mapper.RegionGroupMessageMapper;
import xyz.qy.implatform.service.IRegionGroupMemberService;
import xyz.qy.implatform.service.IRegionGroupMessageService;
import xyz.qy.implatform.service.IRegionGroupService;
import xyz.qy.implatform.service.IUserService;
import xyz.qy.implatform.session.SessionContext;
import xyz.qy.implatform.session.UserSession;
import xyz.qy.implatform.util.BeanUtils;
import xyz.qy.implatform.util.DateTimeUtils;
import xyz.qy.implatform.util.RedisCache;
import xyz.qy.implatform.util.SensitiveUtil;
import xyz.qy.implatform.vo.GroupMessageVO;
import xyz.qy.implatform.vo.RegionGroupMessageVO;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 地区群聊消息
 *
 * @author Polaris
 * @since 2022-11-03
 */
@Slf4j
@Service
public class RegionGroupMessageServiceImpl extends ServiceImpl<RegionGroupMessageMapper, RegionGroupMessage> implements IRegionGroupMessageService {

    @Resource
    private IRegionGroupService regionGroupService;

    @Resource
    private IRegionGroupMemberService regionGroupMemberService;

    @Resource
    private IUserService userService;

    @Resource
    private IMClient imClient;

    @Resource
    private RedisCache redisCache;
    @Override
    public RegionGroupMessageVO sendMessage(RegionGroupMessageDTO dto) {
        UserSession session = SessionContext.getSession();
        RegionGroup regionGroup = regionGroupService.getById(dto.getRegionGroupId());
        if (Objects.isNull(regionGroup)) {
            throw new GlobalException(ResultCode.PROGRAM_ERROR, "群聊不存在");
        }
        if (regionGroup.getDeleted()) {
            throw new GlobalException(ResultCode.PROGRAM_ERROR, "群聊已解散");
        }

        // 根据加入类型判断用户是否在群里
        checkUserInRegionGroup(dto.getJoinType(), session.getUserId(), regionGroup);
        User user = userService.getById(session.getUserId());

        // 查询常驻用户id
        List<Long> userIds = regionGroupMemberService.findUserIdsByRegionGroupId(regionGroup.getId());

        // 查询临时用户id
        Collection<String> keys = redisCache.keys(IMRedisKey.IM_REGION_GROUP_NUM_TEMP_USER + regionGroup.getCode() + ":" + regionGroup.getNum() + ":*");
        for (String key : keys) {
            Object object = redisCache.getCacheObject(key);
            if (ObjectUtil.isNotNull(object)) {
                UserSession userSession = Convert.convert(UserSession.class, object);
                userIds.add(userSession.getUserId());
            }
        }

        // 不用发给自己
        userIds = userIds.stream().filter(id -> !session.getUserId().equals(id)).collect(Collectors.toList());
        // 保存消息
        RegionGroupMessage msg = BeanUtils.copyProperties(dto, RegionGroupMessage.class);
        msg.setSendId(session.getUserId());
        msg.setSendTime(new Date());
        msg.setSendNickName(user.getNickName());
        if (MessageType.TEXT.code().equals(msg.getType())) {
            msg.setContent(SensitiveUtil.filter(msg.getContent()));
        }
        if (CollUtil.isNotEmpty(dto.getAtUserIds())) {
            msg.setAtUserIds(StrUtil.join(",", dto.getAtUserIds()));
        }
        this.save(msg);
        // 不用发给自己
        userIds = userIds.stream().filter(id -> !session.getUserId().equals(id)).collect(Collectors.toList());
        // 群发
        RegionGroupMessageVO msgInfo = BeanUtils.copyProperties(msg, RegionGroupMessageVO.class);
        msgInfo.setAtUserIds(dto.getAtUserIds());
        IMRegionGroupMessage<RegionGroupMessageVO> sendMessage = new IMRegionGroupMessage<>();
        sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
        sendMessage.setRecvIds(userIds);
        sendMessage.setSendResult(false);
        sendMessage.setData(msgInfo);
        imClient.sendRegionGroupMessage(sendMessage);
        log.info("发送群聊消息，发送id:{},群聊id:{},内容:{}", session.getUserId(), dto.getRegionGroupId(), dto.getContent());
        return null;
    }

    private void checkUserInRegionGroup(Integer joinType, Long userId, RegionGroup regionGroup) {
        // 临时加入的判断地区群聊的临时用户是否有当前用户
        if (joinType == 0) {
            Object object = redisCache.getCacheObject(IMRedisKey.IM_REGION_GROUP_NUM_TEMP_USER + regionGroup.getCode() + ":" + regionGroup.getNum() + ":" + userId);
            if (ObjectUtil.isNull(object)) {
                throw new GlobalException("您当前未加入当前地区群聊或在线时长已到期");
            }
        } else {
            RegionGroupMember regionGroupMember = regionGroupMemberService
                    .findByRegionGroupIdAndUserId(regionGroup.getId(), userId);
            if (ObjectUtil.isNull(regionGroupMember) || regionGroupMember.getQuit()) {
                throw new GlobalException("您已不在当前地区群聊里面，无法发送消息");
            }
        }
    }

    @Override
    public void pullOfflineMessage(Long minId) {
        UserSession session = SessionContext.getSession();
        List<RegionGroupMember> members = regionGroupMemberService.findNoQuitByUserId(session.getUserId());

        if (CollectionUtil.isEmpty(members)) {
            return;
        }
        Map<Long, RegionGroupMember> groupMemberMap = CollStreamUtil.toIdentityMap(members, RegionGroupMember::getRegionGroupId);
        Set<Long> ids = groupMemberMap.keySet();

        // 只能拉取最近1个月的
        Date minDate = DateTimeUtils.addMonths(new Date(), -1);
        LambdaQueryWrapper<RegionGroupMessage> wrapper = Wrappers.lambdaQuery();
        wrapper.gt(RegionGroupMessage::getId, minId)
                .gt(RegionGroupMessage::getSendTime, minDate)
                .in(RegionGroupMessage::getRegionGroupId, ids)
                .ne(RegionGroupMessage::getStatus, MessageStatus.RECALL.code())
                .orderByAsc(RegionGroupMessage::getId)
                .last("limit 1000");

        List<RegionGroupMessage> messages = this.list(wrapper);

        // 转成vo
        List<RegionGroupMessageVO> vos = messages.stream()
                .map(m -> {
                    RegionGroupMessageVO vo = BeanUtils.copyProperties(m, RegionGroupMessageVO.class);
                    // 被@用户列表
                    if (StringUtils.isNotBlank(m.getAtUserIds()) && Objects.nonNull(vo)) {
                        List<String> atIds = Splitter.on(",").trimResults().splitToList(m.getAtUserIds());
                        vo.setAtUserIds(atIds.stream().map(Long::parseLong).collect(Collectors.toList()));
                    }
                    return vo;
                })
                .collect(Collectors.toList());
        for (RegionGroupMessageVO vo : vos) {
            IMGroupMessage<RegionGroupMessageVO> sendMessage = new IMGroupMessage<>();
            sendMessage.setSender(new IMUserInfo(vo.getSendId(), IMTerminalType.WEB.code()));
            sendMessage.setRecvIds(Collections.singletonList(session.getUserId()));
            sendMessage.setRecvTerminals(Collections.singletonList(session.getTerminal()));
            sendMessage.setSendResult(false);
            sendMessage.setSendToSelf(false);
            sendMessage.setData(vo);
            imClient.sendGroupMessage(sendMessage);
        }
    }
}
